package top.fyukina.fymil.internal.exps;

import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.Text;
import top.fyukina.fymil.elements.Environment;
import java.math.BigDecimal;
import static top.fyukina.fymil.internal.exps.CompareExp.CompareCalc.*;
import static top.fyukina.fymil.parsers.BooleanParser.BoolCalc.AND;

public class CompareExp implements Expression<Boolean> {
    @Override
    public String registerName() {
        return "c";
    }
    @Override
    public Boolean parse(Element tag, Environment env) {
        Object lastObj = null;
        boolean rst = true;
        CompareCalc op = null;
        for (Node node : tag.content()) {
            if (node instanceof Element el0) {
                switch (el0.getName()) {
                    case "eq" -> op = EQUAL;
                    case "neq" -> op = NOT_EQUAL;
                    case "gt" -> op = GREATER_THAN;
                    case "lt" -> op = LOWER_THAN;
                    case "gteq" -> op = GREATER_EQUAL;
                    case "lteq" -> op = LOWER_EQUAL;
                    default -> {
                        Object r = env.reg().expParser.parse(el0, env);
                        if (lastObj == null) {
                            //首个元素进入lastObj
                            lastObj = r;
                        } else if (op != null) {
                            rst = env.reg().boolParser.
                                    nextCalcBool(rst, compareObj(lastObj, r, op), AND);
                            //比较符用掉之后及时清除，保证元素一对一比较
                            op = null;
                        }
                        //否则:无效的表达式，如:
                        //<n>5</n> <eq> <n>7</n><n>2</n>
                    }
                }
            } else if (node instanceof Text t) {
                if (t.getText().isBlank()) {
                    continue;
                }
                Object r = env.reg().comParser.
                        parseTry(env.reg().strParser.realTrim(t.getText()), env);
                if (lastObj == null) {
                    lastObj = r;
                } else if (op != null) {
                    rst = env.reg().boolParser
                            .nextCalcBool(rst, compareObj(lastObj, r, op), AND);
                    op = null;
                }
            }
        }
        return rst;
    }
    public static boolean compareObj(Object obj1, Object obj2, CompareCalc op) {
        if (obj1 == null || obj2 == null) {
            return false;
        }
        switch (op) {
            case EQUAL -> {
                if (obj1 instanceof BigDecimal bd) {
                    if (obj2 instanceof BigDecimal bd2) {
                        return bd.compareTo(bd2) == 0;
                    }
                    if (obj2 instanceof Boolean bl) {
                        return bl == !bd.equals(BigDecimal.ZERO);
                    }
                    return false;
                }
                if (obj1 instanceof String str) {
                    return obj2 instanceof String str2 && str.equals(str2);
                }
                if (obj1 instanceof Boolean bl) {
                    if (obj2 instanceof Boolean bl2) {
                        return bl == bl2;
                    }
                    if (obj2 instanceof BigDecimal bd) {
                        return bl == !bd.equals(BigDecimal.ZERO);
                    }
                    return false;
                }
                return false;
            }
            case NOT_EQUAL -> {
                if (obj1 instanceof BigDecimal bd) {
                    if (obj2 instanceof BigDecimal bd2) {
                        return bd.compareTo(bd2) != 0;
                    }
                    if (obj2 instanceof Boolean bl) {
                        return bl == bd.equals(BigDecimal.ZERO);
                    }
                    return false;
                }
                if (obj1 instanceof String str) {
                    return obj2 instanceof String str2 && !str.equals(str2);
                }
                if (obj1 instanceof Boolean bl) {
                    if (obj2 instanceof Boolean bl2) {
                        return bl != bl2;
                    }
                    if (obj2 instanceof BigDecimal bd) {
                        return bl == bd.equals(BigDecimal.ZERO);
                    }
                    return false;
                }
                return false;
            }
            case GREATER_THAN -> {
                if (obj1 instanceof BigDecimal bd) {
                    return obj2 instanceof BigDecimal bd2 && bd.compareTo(bd2) > 0;
                }
                if (obj1 instanceof String str) {
                    return obj2 instanceof String str2 && str.length() > str2.length();
                }
                return false;
            }
            case LOWER_THAN -> {
                if (obj1 instanceof BigDecimal bd) {
                    return obj2 instanceof BigDecimal bd2 && bd.compareTo(bd2) < 0;
                }
                if (obj1 instanceof String str) {
                    return obj2 instanceof String str2 && str.length() < str2.length();
                }
                return false;
            }
            case GREATER_EQUAL -> {
                if (obj1 instanceof BigDecimal bd) {
                    return obj2 instanceof BigDecimal bd2 && bd.compareTo(bd2) >= 0;
                }
                if (obj1 instanceof String str) {
                    return obj2 instanceof String str2 && str.length() >= str2.length();
                }
                return false;
            }
            case LOWER_EQUAL -> {
                if (obj1 instanceof BigDecimal bd) {
                    return obj2 instanceof BigDecimal bd2 && bd.compareTo(bd2) <= 0;
                }
                if (obj1 instanceof String str) {
                    return obj2 instanceof String str2 && str.length() <= str2.length();
                }
                return false;
            }
            default -> {
                return false;
            }
        }
    }
    public enum CompareCalc {
        EQUAL, NOT_EQUAL, GREATER_THAN, LOWER_THAN, GREATER_EQUAL, LOWER_EQUAL
    }
}